/******************************************************************************
 * \brief	私有通信协议服务器端函数接口
 * \note	File format: UTF-8，中文编码：UTF-8
 * \author	将狼才鲸
 * \date	2023-03-25
 ******************************************************************************/

/*********************************** 头文件 ***********************************/
#include "scp_server.h"
#include <stdio.h>	/* puts */

/*********************************** 宏定义 ***********************************/
#define scpsrv_packet_declare(pid)	static int scpsrv_packet_##pid(scp_handle hdl, seg_pkt_head_t *pkt)
#define scpsrv_packet_call(pid)	case pid: {ack = scpsrv_packet_##pid(hdl, pkt);} break

/********************************** 私有函数 **********************************/
/**
 * \brief	回环包处理请求包的操作，响应包中赋值请求包对应的ID
 * \details	等同于static int scpsrv_packet_SCP_PG_SCP_LOOPBACK(scp_handle hdl, seg_pkt_head_t *pkt)
 * \param	hdl:	通信句柄
 * \param	pkt:	接收到的包头
 */
scpsrv_packet_declare(SCP_PG_DEBUG_LOOPBACK)
{
	pkt->PID = htons(SCP_RPID(SCP_PG_DEBUG_LOOPBACK));
	pkt->SUB = pkt->SID;

	return ACK_NULL;
}

/**
 * \brief	调试打印输出包
 * \details	等同于static int scpsrv_packet_SCP_PG_DEBUG_PUTS(scp_handle hdl, seg_pkt_head_t *pkt)
 * \param	hdl:	通信句柄
 * \param	pkt:	接收到的包头
 */
scpsrv_packet_declare(SCP_PG_DEBUG_PUTS)
{
	long len = ntohl(pkt->LEN);
	char *str = (char *)(((uint8_t *)pkt) + SEG_PKT_HEAD_LEN);
	str[len] = 0;
	puts(str);

	return ACK_ACK1;
}

/**
 * \brief	处理请求包的操作，响应包中赋值请求包对应的ID
 * \param	hdl:	通信句柄
 * \param	pkt:	收到的包
 * \return	协议SUB ID
 */
static uint8_t scpsrv_pkt_parse(scp_handle hdl, seg_pkt_head_t *pkt)
{
	uint8_t ack = ACK_NACK;
	uint16_t pid = ntohs(pkt->PID);	/* 应用层协议ID */

	/* 权限控制 */
	int sec_level = -1;
	if (pkt->SEC & SEC_UG)
		sec_level = SECLVL_URGENCY;
	else if (pkt->SEC & (SEC_SW | SEC_SR))
		sec_level = SECLVL_ADMIN;
	else if (pkt->SEC & (SEC_TW | SEC_TR))
		sec_level = SECLVL_OPERATOR;
	else if (pkt->SEC & (SEC_PW | SEC_PR))
		sec_level = SECLVL_USER;

	/* 检查权限和通信协议（很少会用到） */
	if (sec_level < 0 || sec_level >= SECLVL_COUNT) {
		pr_err("\n\tSEC not valid for the packet !\n");
	}
	else if (pkt->TYPE != SCP_TYPE) {
		pr_err("\n\tTYPE not valid for the packet !\n");
	}
#if defined(CONFIG_SCP_USE_CRC32)
	//TODO: 该函数暂未实现
	// else if (!scp_pkt_crc(pkt, false)) {
	// 	pr_err("\n\tCRC error for the packet !\n");
	// }
#endif
	else {
		switch (pid) {
			/* 里面会赋值ack变量 */
			scpsrv_packet_call(SCP_PG_DEBUG_LOOPBACK);	/* scpsrv_packet_declare(SCP_PG_DEBUG_LOOPBACK) */
			scpsrv_packet_call(SCP_PG_DEBUG_PUTS);
			// scpsrv_packet_call(SCP_PG_DEBUG_GETCHAR);

			// scpsrv_packet_call(SCP_PG_SYSTEM_IOR);
			// scpsrv_packet_call(SCP_PG_SYSTEM_IOW);
			// scpsrv_packet_call(SCP_PG_SYSTEM_IOM);
			// scpsrv_packet_call(SCP_PG_SYSTEM_EXIT);

			// scpsrv_packet_call(SCP_PG_MSG_READ);
			// scpsrv_packet_call(SCP_PG_MSG_WRITE);
		};
	}

	return ack;
}

/********************************** 接口函数 **********************************/
/**
 * \brief   当前为空函数
 */
void scpsrv_init(void)
{
	/* 打开服务器端只在通信协议中才支持的一些功能，如GUI界面的命令控制等 */
}

/**
 * \brief	服务器端轮询响应消息
 * \details	查询客户端发来的消息，发回ACK包，或者返回处理后需要返回的数据包
 */
int scpsrv_run(scp_handle handle)
{
	int res;
	uint8_t ack;
	scp_dev_t *pdev = NULL;
	seg_pkt_head_t *pkt;
	segment_t *seg;

	scp_t *pscp = (scp_t *)handle;
	if (!scp_valid(pscp))
		return -EACCES;

	pdev = (scp_dev_t *)(pscp->pdev);
	if (!scpdev_valid(pdev, SCP_DEVTYPE_ANY))
		return -EACCES;

	/* 轮询的读消息，一次轮询不一定读到完整的包，可能多次轮询后才能收满包 */
	seg = scpdevs_read(pdev, RWMODE_REQUEST);
	if (!seg)
		return 0;	/* 未收到包或未收满包则直接返回 */

	/* 处理读到的消息 */
	pkt = (seg_pkt_head_t *)scpseg_data(seg);

	/* 处理请求包的操作，响应包中赋值请求包对应的ID */
	ack = scpsrv_pkt_parse(handle, pkt);
	if (ack != ACK_NULL) {
		/* 如果是正确的响应包，即ack等于ACK_NACK或ACK_ACK1，原始包不是SCP_PG_DEBUG_LOOPBACK */
		memcpy(pkt + 1, pkt, sizeof(seg_pkt_head_t));	/* 将原发送来的包头复制一份放在响应包头后面的位置 */
		/* 协议ID改成包响应，原始协议ID在紧跟的第二包中有保留 */
		pkt->PID = htons(SCP_PID_ACK);
		pkt->SUB = htonl(ack);	/* 响应包类型，ACK_NACK、 */
		pkt->LEN = htonl(sizeof(seg_pkt_head_t));	/* 包长度只算响应包的包头 */
	}
	seg->LEN = sizeof(seg_pkt_head_t) + ntohl(pkt->LEN);

	/* 发送响应包 */
	res = scpdevs_send(seg, RWMODE_RESPOND);

	if (seg->respseg && seg->respseg != seg)
		scpseg_free(seg->respseg);	/* 如果有响应包则删除 */

	scpseg_free(seg);	/* 删除收到的包 */

	if (ack == ACK_EXTA)
		return -ECANCELED;

	return res;
}

/*********************************** 文件尾 ***********************************/
